//
// Created by Asus on 2022-04-16.
//
#include <jni.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include "base64.h"
#include <sys/ptrace.h>
//#include <SKF.h>
#include <skfapi.h>
//#include "skfapi.h"
#include <stdbool.h>
#include "logger.h"
//#include "APDUs.h"

// 获取数组的大小
# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
// 指定要注册的类，对应完整的java类名
#define JNIREG_CLASS "com/tongxin/sdencrypt/SimEncrypt"

jstring charToJstring(JNIEnv *envPtr, char *src) {
    JNIEnv env = *envPtr;

    jsize len = strlen(src);
    jclass clsstring = env->FindClass(envPtr, "java/lang/String");
    jstring strencode = env->NewStringUTF(envPtr, "UTF-8");
    jmethodID mid = env->GetMethodID(envPtr, clsstring, "<init>", "([BLjava/lang/String;)V");
    jbyteArray barr = env->NewByteArray(envPtr, len);
    env->SetByteArrayRegion(envPtr, barr, 0, len, (jbyte *) src);

    return (jstring) env->NewObject(envPtr, clsstring, mid, barr, strencode);
}
static DEVHANDLE          pathandle=NULL;
static HAPPLICATION       patapphandle=NULL;
//默认key："00112233445566778899AABBCCDDEEFF"
static char DevAuthKey[33] = {0};

JNIEXPORT jstring JNICALL enum_dev(JNIEnv *env, jobject instance) {
    char *appName = "SIMKey";
    char *userPin = "123456";

    int ret = SAR_FAIL;

    //连接设备
    ret = skfa_connectDevice();
    if (SAR_OK != ret)
    {
        LOGI("skfa_connectDevice fail! \n");
        return ret;
    }

    char tmpbuf1[512] = {0};
    ULONG recLen = 512;
    //枚举应用
    ret = SKF_EnumApplication(pathandle,tmpbuf1,&recLen);
    if(SAR_OK != ret)
    {
        LOGE("SKF_EnumApplication fail! \n");
        return ret;
    }
    LOGI("SKF_EnumApplication appName: %s\n", tmpbuf1);
    //打开应用
        ret = SKF_OpenApplication(pathandle, appName, &patapphandle);
        if(SAR_OK != ret)
        {
            LOGE("SKF_OpenApplication fail! \n");
            return ret;
        }

    HCONTAINER hContainer;
   // PCERTTfICATEINFO ptrCer = NULL;
    char names[200] = {0}, *ptrName = NULL;
    ULONG lenNames = 200;

    ULONG tempLen = 512;
    //校验用户pin
    ret = SKF_VerifyPIN(patapphandle,USER_TYPE,userPin,&tempLen);
    if (SAR_OK != ret) {
        LOGE("SKF_VerifyPIN fail! \n");
        return ret;
    }


    char *containerName = "SIMKeyContainer";
//    ret = SKF_CreateContainer(patapphandle, containerName, &hContainer);
//    if (ret != SAR_OK) {
//        LOGE("SKF_CreateContainer fail! \n");
//        return ret;
//    }

    //枚举容器
    ret = SKF_EnumContainer(patapphandle, names, &lenNames);
    if (SAR_OK != ret) {
        LOGE("SKF_EnumContainer fail! \n");
        return ret;
    }

    ret = SKF_OpenContainer(patapphandle, containerName, &hContainer);
    if (ret != SAR_OK) {
        LOGE("SKF_OpenContainer fail! \n");
        return ret;
    }

    ULONG containerType;
    //获取容器类型
    ret = SKF_GetContainerType(hContainer, &containerType);
    if (SAR_OK != ret) {
        LOGE("SKF_GetContainerType fail! \n");
        return ret;
    }
 /*
    //生成会话密钥
    //SM2
    ret = SKF_ECCExportSessionKey(hContainer, SGD_SM4_ECB, (PECCPUBLICKEYBLOB)pbPubkey, (PECCCIPHERBLOB)pbEnSymkey, &hKey);
    if (SAR_OK == ret) {
        //真正的返回数据长度，是结构体的长度，加上其中加密数据的长度
        *pLenEnSymkey = sizeof(ECCCIPHERBLOB)+((PECCCIPHERBLOB)pbEnSymkey)->CipherLen-1;
    }
*/


//    ret = skfa_genKeyPairECC(hContainer, pbPlain, ulLenPlain, (PECCPUBLICKEYBLOB)pbPubkey, (PECCSIGNATUREBLOB)pbSign);
//    if (SAR_OK == ret) {
//        *pulLenPubkey = sizeof(ECCPUBLICKEYBLOB);
//        *pulLenSign = sizeof(ECCSIGNATUREBLOB);
//    }




    return SAR_OK;

//    LOGI("pathandle: %d", pathandle);
//    jstring  result = charToJstring(env, tmpbuf1);
//    LOGI("tmpbuf1: %ld", tmpbuf1);
//    LOGI("recLen: %d", recLen);
//    return result;
}

JNIEXPORT jstring JNICALL skfa_connectSimkeyApp(JNIEnv *env, jobject instance)
{
//    char *appName = (char *) (*env)->GetStringUTFChars(env, appName_, JNI_FALSE);
//    if (appName == NULL) {
//        LOGE("skfa_connectSimkeyApp with null string.");
//        return -1;
//    }
//    LOGI("skfa_connectSimkeyApp appName: %s\n", appName);
//
//    char *userPin = (char *) (*env)->GetStringUTFChars(env, userPin_, JNI_FALSE);
//    if (userPin == NULL) {
//        LOGE("skfa_connectSimkeyApp with null string.");
//        return -1;
//    }
//    LOGI("skfa_connectSimkeyApp userPin: %s\n", userPin);
    char *appName = "123456";
    char *userPin = "SIMKey";

    int ret = SAR_FAIL;
    char tmpbuf1[512] = {0};
    ULONG recLen = 512;

    if (appName == NULL || userPin == NULL)
    {
        return SAR_FAIL;
    }

    //连接设备
    ret = skfa_connectDevice();
    if (SAR_OK != ret)
    {
        LOGE("skfa_connectDevice fail! \n");
        return ret;
    }

    //枚举应用
    ret = SKF_EnumApplication(pathandle,tmpbuf1,&recLen);
    if(SAR_OK != ret)
    {
        LOGE("SKF_EnumApplication fail! \n");
        return ret;
    }
    LOGI("SKF_EnumApplication appName: %s\n", tmpbuf1);

//    if (isContainString(tmpbuf1, appName)) {
        //打开应用
//        ret = SKF_OpenApplication(pathandle, appName, &patapphandle);
//        if(SAR_OK != ret)
//        {
//            LOGE("SKF_OpenApplication fail! \n");
//            return ret;
//        }
//    } else {
//        //创建应用
//        ret = skfa_createApplication(appName, userPin);
//        if(SAR_OK != ret)
//        {
//            LOGE("skfa_createApplication fail! \n");
//            return ret;
//        }
//    }
    return SAR_OK;
}


int skfa_connectDevice(void)
{
    char tmpbuf1[512] = {0};
    ULONG recLen = 512;
    int ret = SAR_FAIL;

    //枚举设备
    ret = SKF_EnumDev(0,tmpbuf1,&recLen);
    if (SAR_OK != ret) {
        LOGI("SKF_EnumDev fail! \n");
        return ret;
    }

    //连接设备
    ret = SKF_ConnectDev(tmpbuf1,&pathandle);
    if(SAR_OK != ret)
    {
        LOGI("SKF_ConnectDev fail! \n");
        return ret;
    }

    return SAR_OK;
}

int skfa_createApplication(char *appName,char *userPin)
{
    //最多只有3个应用
    //应用名，最大32字节
    //PIN，最大16字节

    char *adminPin = "123456";
    DWORD adminPinRetryCount = 3;
    DWORD userPinRetryCount = 3;
    DWORD fileRights = SECURE_ANYONE_ACCOUNT;
    int ret = SAR_FAIL;

    if (appName == NULL || userPin == NULL) {
        return SAR_FAIL;
    }

    //创建应用前要进行设备认证
    ret = skfa_authDevice();
    if(SAR_OK != ret)
    {
        LOGI("SKF_DevAuth fail! \n");
        return ret;
    }

    //创建新应用
    ret = SKF_CreateApplication(pathandle, appName, adminPin, adminPinRetryCount, userPin, userPinRetryCount, fileRights, &patapphandle);
    if (SAR_OK != ret) {
        LOGI("SKF_CreateApplication fail! \n");
        return SAR_FAIL;
    }

    return SAR_OK;
}

int skfa_authDevice(void)
{
    int ret = -1;
    BYTE random[16] = {0};
    BYTE pbRandomEnc[16] = {0};
    BYTE pbDevAuthKey[16] = {0};


    //生成8字节随机数
    ret = SKF_GenRandom(pathandle, random, 8);
    if (SAR_OK != ret) {
        LOGI("SKF_GenRandom error");
        return SAR_GENRANDERR;
    }

    //加密随机数的密钥
    if(0 == strlen(DevAuthKey))
    {
        memcpy(DevAuthKey,"00112233445566778899AABBCCDDEEFF",32);
    }

    //加密随机数
    asc2hex(pbDevAuthKey, DevAuthKey,strlen(DevAuthKey));
    sms4_encrypt(pbDevAuthKey,random,pbRandomEnc);
    //char keyValueEnc[33] = {0};
    //hex2asc(pbKeyValueEnc, 16, keyValueEnc, 32);

    //设备认证
    ret = SKF_DevAuth(pathandle, pbRandomEnc, 16);
    if (SAR_OK != ret) {
        LOGI("SKF_DevAuth error");
        return SAR_FAIL;
    }

    return SAR_OK;
}

//JNIEXPORT jint JNICALL connect_dev(JNIEnv *env, jobject instance, jstring str_) {
//    char *szDrive = (char *) (*env)->GetStringUTFChars(env, str_, JNI_FALSE);
//    if (szDrive == NULL) {
////        LOGE("connect_dev with null string.");
//        return -1;
//    }
////    LOGI("connect_dev szDrive: %s\n", szDrive);
//    DEVHANDLE pulDriveNum = 0;
//    unsigned long baseResult = SKF_ConnectDev(szDrive, &pulDriveNum);
////    LOGI("connect_dev baseResult: %ld", baseResult);
////    LOGI("connect_dev pulDriveNum: %d", pulDriveNum);
////    sv_Device = pulDriveNum;
//    (*env)->ReleaseStringUTFChars(env, str_, szDrive);
//    if (baseResult == 0) {
//        return pulDriveNum;
//    } else {
//        return baseResult;
//    }
//}

JNIEXPORT jint JNICALL connect_dev(JNIEnv *env, jobject instance) {
    //关闭应用，将相关参数置空
    ULONG ret = SAR_OK;
    ret = SKF_CloseApplication(patapphandle);
    ret = SKF_DisConnectDev(pathandle);
    pathandle = NULL;
    patapphandle = NULL;
    return ret;
}



// Java和JNI函数的绑定表
static JNINativeMethod method_table[] = {
//        {"setPackageName",  "(Ljava/lang/String;)J",                                   (void *) set_package},
//        {"GetFuncList",     "()Ljava/lang/String;",                                    (void *) get_func_list},
//        {"ImportCert",      "(I[B)J",                                                  (void *) import_cert},
//        {"ExportCert",      "(I)[B",                                                   (void *) export_cert},
        {"EnumDev",         "()Ljava/lang/String;",                                    (void *) enum_dev},
        {"ConnectDev",      "()I",                                   (void *) connect_dev},
        {"skfaCreateApplication",      "()I",                        (void *) skfa_connectSimkeyApp},
//        {"DisconnectDev",   "(I)J",                                                    (void *) disconnect_dev},
//        {"GenRandom",       "(I)Ljava/lang/String;",                                   (void *) gen_random},
//        {"GenECCKeyPair",   "(I)[B",                                                   (void *) gen_ecc_key},
//        {"ImportECCKey",    "(I[B[B)J",                                                (void *) import_ecc_key},
//        {"ECCSignData",     "(I[B)[B",                                                 (void *) ecc_sign_data},
//        {"ECCVerify",       "(I[B[B)J",                                                (void *) ecc_verify},
//        {"ExtECCVerify",    "(I)J",                                                    (void *) ext_ecc_verify},
//        {"GenDataWithECC",   "(I)J",                                                    (void *) gen_data_ecc},
//        {"GenKeyWithECC",    "(I)J",                                                    (void *) gen_key_ecc},
//        {"GenDataAndKeyWithECC", "(I)J",                                                (void *) gen_data_key_ecc},
//        {"ExportPublicKey",   "(I)[B",                                                  (void *) export_public_key},
//        {"ImportSessionKey",  "(I)J",                                                   (void *) import_session_key},
//        {"SetSymKey",         "(I[B)J",                                                 (void *) set_sym_key},
//        {"GetSymKey",         "(I)Ljava/lang/String;",                                  (void *) get_sym_key},
//        {"CheckSymKey",       "(I)J",                                                   (void *) check_sym_key},
//        {"CloseHandle",       "(I)J",                                                   (void *) close_handle},
//        {"GetDevInfo",        "(I)Ljava/lang/String;",                                  (void *) get_dev_info},
//        {"GetZA",             "(I[B)J",                                                 (void *) get_za},
//        {"EncryptInit",       "(I)J",                                                   (void *) encrypt_init},
//        {"Encrypt",           "(I[B)[B",                                                (void *) encrypt},
//        {"EncryptUpdate",     "(I)J",                                                   (void *) encrypt_update},
//        {"EncryptFinal",      "(I)J",                                                   (void *) encrypt_final},
//        {"DecryptInit",       "(I)J",                                                   (void *) decrypt_init},
//        {"Decrypt",           "(I)J",                                                   (void *) decrypt},
//        {"DecryptUpdate",     "(I)J",                                                   (void *) decrypt_update},
//        {"DecryptFinal",      "(I)J",                                                   (void *) decrypt_final},
//        {"DigestInit",        "(I)J",                                                   (void *) digest_init},
//        {"Digest",            "(I)J",                                                   (void *) digest},
//        {"DigestUpdate",      "(I)J",                                                   (void *) digest_update},
//        {"DigestFinal",       "(I)J",                                                   (void *) digest_final},
//        {"MacInit",           "(I)J",                                                   (void *) mac_init},
//        {"MacUpdate",         "(I)J",                                                   (void *) mac_update},
//        {"MacFinal",          "(I)J",                                                   (void *) mac_final},
//        {"GenerateKey",       "(I)J",                                                   (void *) gen_key},
//        {"ECCExportSessionKey", "(I)J",                                                 (void *) ecc_export_session_key},
//        {"ECCPrvKeyDecrypt",  "(I)J",                                                   (void *) ecc_prv_key_decrypt},
//        {"ImportKeyPair",     "(I)J",                                                   (void *) import_key_pair},
//        {"Cipher",            "(I)J",                                                   (void *) cipher},
};

// 注册native方法到java中
static int registerNativeMethods(JNIEnv *env, const char *className,
                                 JNINativeMethod *gMethods, int numMethods) {
    jclass clazz;
    clazz = (*env)->FindClass(env, className);
    if (clazz == NULL) {
        return JNI_FALSE;
    }
    if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
        return JNI_FALSE;
    }

    return JNI_TRUE;
}

JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {

//    ptrace(PTRACE_TRACEME, 0, 0, 0);

JNIEnv *env = NULL;
jint result = -1;

if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_4) != JNI_OK) {
return result;
}

// call register method
if (registerNativeMethods(env, JNIREG_CLASS, method_table, NELEM(method_table)) <= 0) {
return result;
}

// return jni version
return JNI_VERSION_1_4;
}


